package io.qar.service.impl;

import cn.hutool.extra.ftp.Ftp;
import cn.hutool.extra.ftp.FtpMode;
import io.qar.commons.tools.validator.ValidatorUtils;
import io.qar.config.ftpUtil.FileEntity;
import io.qar.config.ftpUtil.TreeFtpUtils;
import io.qar.dto.CfgFtpSettingDTO;
import io.qar.dto.FtpParamDTO;
import io.qar.dto.FtpToFtpDTO;
import io.qar.entity.CfgSrcFtpFileEntity;
import io.qar.service.CfgFtpSettingService;
import io.qar.service.FtpService;
import io.qar.service.func.ConverterValue;
import io.qar.utils.FtpUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @program: qar-cloud
 * @description:
 * @author: yyyyjinying
 * @create: 2023-10-30 11:08
 **/
@Slf4j
@Service
public class FtpServiceImpl implements FtpService {
    @Autowired
    private FtpUtils ftpUtils;
    @Autowired
    private CfgFtpSettingService cfgFtpSettingService;

    @Override
    public FTPClient getFtpClient(FtpParamDTO ftpParamDTO) {
        String host = ftpParamDTO.getHost();
        int port = ftpParamDTO.getPort();
        String username = ftpParamDTO.getUsername();
        String password = ftpParamDTO.getPassword();
        // 创建FTP客户端
        Ftp ftp = new Ftp(host, port, username, password, StandardCharsets.UTF_8);
        // 设置传输模式
        ftp.setMode(FtpMode.Passive);
        FTPClient client = ftp.getClient();
        if (client == null) {
            try {
                ftp.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return client;
    }

    public String getFileSuffix(String val) {
        if (val.contains(".")) {
            int i = val.lastIndexOf(".");
            return val.substring(i);
        }
        return null;
    }

    private void setAllFileNodes(FTPClient ftpClient, String strId, String path, List<FileEntity> nodeList) {
        path = path + File.separator;
        try {
            boolean b = ftpClient.changeWorkingDirectory(path);
            if (b) {
                FTPFile[] ftpFiles = ftpClient.listFiles();
                int num = 0;
                int subNum = 0;
                for (FTPFile ftpFile : ftpFiles) {
                    if (ftpFile.isFile()) {
                        num++;
                        FileEntity fileEntity = new FileEntity();
                        fileEntity.setId(strId + "-" + num);
                        fileEntity.setPid(strId);
                        fileEntity.setName(ftpFile.getName());
                        fileEntity.setLabel(path + ftpFile.getName());
                        fileEntity.setOrderBy(String.valueOf(num));
                        fileEntity.setFilePath(path);
                        fileEntity.setType(ftpFile.getType());
                        fileEntity.setSize(ftpFile.getSize());
                        fileEntity.setFileSuffix(getFileSuffix(ftpFile.getName()));
                        fileEntity.setFilename(ftpFile.getName());
                        fileEntity.setValue(path + ftpFile.getName());
                        nodeList.add(fileEntity);
                        continue;
                    }
                    if (ftpFile.isDirectory()) {
                        subNum++;

                        String value = "";
                        String parentValue = "0";
                        if (strId == "0") {
                            Integer id = Integer.valueOf(strId) + 1;
                            parentValue = String.valueOf(id);
                            value = parentValue + "-" + String.valueOf(subNum);
                        } else {
                            parentValue = strId;
                            value = parentValue + "-" + String.valueOf(subNum);
                        }

                        FileEntity fileEntity = new FileEntity();
                        String sb = path + ftpFile.getName();
                        fileEntity.setId(value);
                        fileEntity.setPid(parentValue);
                        fileEntity.setName(ftpFile.getName());
                        fileEntity.setLabel(sb);
                        fileEntity.setOrderBy(String.valueOf(num));
                        fileEntity.setFilePath(sb);
                        fileEntity.setType(ftpFile.getType());
                        fileEntity.setSize(ftpFile.getSize());
                        nodeList.add(fileEntity);
                        // 递归
                        setAllFileNodes(ftpClient, value, sb, nodeList);
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void setAllSrcFileNodes(FTPClient ftpClient, String strId, String path, List<CfgSrcFtpFileEntity> nodeList, ConverterValue<CfgSrcFtpFileEntity> c) {
        path = path + File.separator;
        try {
            boolean b = ftpClient.changeWorkingDirectory(path);
            if (b) {
                FTPFile[] ftpFiles = ftpClient.listFiles();
                int num = 0;
                for (FTPFile ftpFile : ftpFiles) {
                    num++;
                    String pid = strId;
                    String id = strId + "-" + num;
                    if (ftpFile.isFile()) {
                        if (ftpUtils.hasConditionSize(ftpFile.getSize()) && ftpUtils.hasConditionValue(ftpFile.getName())) {
                            nodeList.add(c.getValue(ftpFile, id, pid, path, num));
                        }
                    }
                    if (ftpFile.isDirectory()) {
                        String sb = path + ftpFile.getName();
                        CfgSrcFtpFileEntity cfgSrcFtpFileEntity = c.getValue(ftpFile, id, pid, path, num);
                        nodeList.add(cfgSrcFtpFileEntity);
                        // 递归
                        setAllSrcFileNodes(ftpClient, id, sb, nodeList, c);
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 获取FTP树形结构
    @Override
    public List<FileEntity> getFileEntityList(FtpParamDTO ftpParamDTO) {

        FTPClient ftpClient = getFtpClient(ftpParamDTO);
        List<FileEntity> nodeList = new ArrayList<>();
        setAllFileNodes(ftpClient, "0", "", nodeList);
        // 转换树形结构
        List<FileEntity> res = TreeFtpUtils.build(nodeList, "1");
        return res;
    }

    /**
     * 指定pid 创建层级列表
     * @param ftpParamDTO
     * @param pidNum
     * @return
     */
    @Override
    public List<CfgSrcFtpFileEntity> getAllTreeFileLists(FtpParamDTO ftpParamDTO, int pidNum) {
        FTPClient ftpClient = getFtpClient(ftpParamDTO);
        List<CfgSrcFtpFileEntity> nodeList = new ArrayList<>();
        setAllSrcFileNodes(ftpClient, String.valueOf(pidNum), "", nodeList, (ftpFile, id, pid, path, num) -> {
            CfgSrcFtpFileEntity cfgSrcFtpFileEntity = new CfgSrcFtpFileEntity();
            cfgSrcFtpFileEntity.setSrcIp(ftpParamDTO.getHost());
            cfgSrcFtpFileEntity.setSrcPort(ftpParamDTO.getPort());
            cfgSrcFtpFileEntity.setSrcUsername(ftpParamDTO.getUsername());
            cfgSrcFtpFileEntity.setSrcPassword(ftpParamDTO.getPassword());
            cfgSrcFtpFileEntity.setId(id);
            cfgSrcFtpFileEntity.setPid(pid);
            String label = ftpFile.getName() != null ? path + ftpFile.getName() : path;
            cfgSrcFtpFileEntity.setName(label);
            cfgSrcFtpFileEntity.setLabel(label);
            cfgSrcFtpFileEntity.setOrderby(num);
            cfgSrcFtpFileEntity.setFilePath(path);
            cfgSrcFtpFileEntity.setType(ftpFile.getType());
            cfgSrcFtpFileEntity.setSize(ftpFile.getSize());
            cfgSrcFtpFileEntity.setFileCreateTime(ftpFile.getTimestamp().getTime());

            // type 1 是文件夹 0 是文件
            if (ftpFile.getType() < 1) {
                cfgSrcFtpFileEntity.setFilename(ftpFile.getName());
                cfgSrcFtpFileEntity.setFileSuffix(getFileSuffix(ftpFile.getName()));
            }

            return cfgSrcFtpFileEntity;
        });
        return nodeList;
    }


    @Override
    public FileEntity getFileEntityById(FtpParamDTO ftpParamDTO, String id) {
        FTPClient ftpClient = getFtpClient(ftpParamDTO);
        List<FileEntity> nodeList = new ArrayList<>();
        setAllFileNodes(ftpClient, "0", "", nodeList);
        List<FileEntity> collect = nodeList.stream().filter(item -> item.getId().equalsIgnoreCase(id)).collect(Collectors.toList());
        if (collect.size() > 0) {
            return collect.get(0);
        }
        return null;
    }


    // 获取指定的节点数据


    @Override
    public boolean ftpToFtpUpload(FtpToFtpDTO ftpToFtpDTO) {
        // 源
        FtpParamDTO srcFtpParamDTO = new FtpParamDTO();
        srcFtpParamDTO.setHost(ftpToFtpDTO.getSrcIp());
        srcFtpParamDTO.setUsername(ftpToFtpDTO.getSrcUsername());
        srcFtpParamDTO.setPassword(ftpToFtpDTO.getSrcPassword());
        srcFtpParamDTO.setPort(ftpToFtpDTO.getSrcPort());
        FTPClient srcFtpClient = getFtpClient(srcFtpParamDTO);
        // 目标
        FtpParamDTO trgFtpParamDTO = new FtpParamDTO();
        trgFtpParamDTO.setHost(ftpToFtpDTO.getTrgIp());
        trgFtpParamDTO.setUsername(ftpToFtpDTO.getTrgUsername());
        trgFtpParamDTO.setPassword(ftpToFtpDTO.getTrgPassword());
        trgFtpParamDTO.setPort(ftpToFtpDTO.getTrgPort());
        FTPClient trgFtpClient = getFtpClient(trgFtpParamDTO);

        InputStream inputStream = retrieveFileStream(srcFtpClient, ftpToFtpDTO.getSrcFilePath(), ftpToFtpDTO.getSrcFileName());
        if (inputStream == null) {
            log.info("输入流获取失败");
            return false;
        }
        boolean pcdata = uploadFileStream(trgFtpClient, ftpToFtpDTO.getTrgFilePath(), ftpToFtpDTO.getTrgFileName(), inputStream);
        return pcdata;
    }

    @Override
    public boolean ftpToFtpTask(long id) {
        CfgFtpSettingDTO cfgFtpSettingDTO = cfgFtpSettingService.get(id);
        FtpToFtpDTO ftpToFtpDTO = new FtpToFtpDTO();
        ftpToFtpDTO.setSrcIp(cfgFtpSettingDTO.getSrcIp());
        ftpToFtpDTO.setSrcPort(cfgFtpSettingDTO.getSrcPort());
        ftpToFtpDTO.setSrcUsername(cfgFtpSettingDTO.getSrcUsername());
        ftpToFtpDTO.setSrcPassword(cfgFtpSettingDTO.getSrcPassword());
        String srcFilePath = cfgFtpSettingDTO.getSrcFilePath().split(cfgFtpSettingDTO.getSrcFilename())[0];
        ftpToFtpDTO.setSrcFilePath(srcFilePath);
        ftpToFtpDTO.setSrcFileName(cfgFtpSettingDTO.getSrcFilename());
        ftpToFtpDTO.setTrgIp(cfgFtpSettingDTO.getTrgIp());
        ftpToFtpDTO.setTrgPort(cfgFtpSettingDTO.getTrgPort());
        ftpToFtpDTO.setTrgUsername(cfgFtpSettingDTO.getTrgUsername());
        ftpToFtpDTO.setTrgPassword(cfgFtpSettingDTO.getTrgPassword());
        String[] split = cfgFtpSettingDTO.getTrgFilePath().split(File.separator + File.separator);
        ftpToFtpDTO.setTrgFilePath(split[0]);
        ftpToFtpDTO.setTrgFileName(split[1]);
        // 验证字段
        ValidatorUtils.validateEntity(ftpToFtpDTO);

        return ftpToFtpUpload(ftpToFtpDTO);
    }

    public InputStream retrieveFileStream(FTPClient client, String remotePath, String fileName) {
        try {
            boolean bol = client.changeWorkingDirectory(remotePath);
            if (bol) {
                return client.retrieveFileStream(fileName);
            }
        } catch (Exception e) {
            log.debug("获取ftp文件流异常，error: {}", e.getMessage());
        }
        return null;
    }

    public boolean uploadFileStream(FTPClient ftpClient, String pathName, String fileName, InputStream inputStream) {
        try {
            if (ftpClient.isConnected()) {
                FTPFile[] ftpFiles = ftpClient.listFiles();
                System.out.println(Arrays.toString(ftpFiles));
                System.out.println("=========");
                if (!ftpClient.changeWorkingDirectory(pathName)) {
                    log.info(pathName + "文件夹在FTP不存在");
                    return false;
                }

                ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
                ftpClient.enterLocalPassiveMode();
                ftpClient.setControlEncoding("UTF-8");
                boolean b = ftpClient.storeFile(fileName, inputStream);
                if (b) {
                    log.info(fileName + "文件上传到FTP成功");
                    inputStream.close();
                    ftpClient.logout();
                } else {
                    log.info(fileName + "文件上传到FTP失败storeFile");
                    inputStream.close();
                    ftpClient.logout();
                }
                return b;

            } else {
                log.error("FTP连接建立失败");
            }
        } catch (Exception e) {
            log.error(fileName + "文件上传异常", e);

        } finally {
            closeFtpClient(ftpClient);
            closeStream(inputStream);
        }
        return false;
    }

    /**
     * 路径切换（没有则创建）
     *
     * @param ftpClient FTP服务器
     * @param path      路径
     */
    public boolean changeAndMakeWorkingDir(FTPClient ftpClient, String path) {
        boolean flag = false;
        try {
            String[] path_array = path.split("/");
            for (String s : path_array) {
                boolean b = ftpClient.changeWorkingDirectory(s);
                if (!b) {
                    ftpClient.makeDirectory(s);
                    ftpClient.changeWorkingDirectory(s);
                }
            }
            flag = true;
        } catch (IOException e) {
            log.error("路径切换异常", e);
        }
        return flag;
    }

    /**
     * 关闭文件流
     *
     * @param closeable
     */
    public void closeStream(Closeable closeable) {
        if (null != closeable) {
            try {
                closeable.close();
            } catch (IOException e) {
                log.error("关闭文件流异常", e);
            }
        }
    }

    /**
     * 关闭FTP连接
     *
     * @param ftpClient
     */
    public void closeFtpClient(FTPClient ftpClient) {
        if (ftpClient.isConnected()) {
            try {
                ftpClient.disconnect();
            } catch (IOException e) {
                log.error("关闭FTP连接异常", e);
            }
        }
    }
}
